package cn.xd.core.utils;

import cn.xd.core.ddd.diff.Diff;
import cn.xd.core.ddd.diff.EntityDiff;
import cn.xd.core.ddd.diff.ListDiff;
import cn.xd.core.ddd.entity.Entity;
import cn.xd.core.ddd.value.IdValue;
import cn.xd.core.ddd.value.basic.Value;
import cn.xd.core.enums.DiffTypeEnum;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;

/**
 * @author mirzaLin
 * @data 2023/11/17
 * @description 差异工具类
 **/
@Slf4j
public class DiffUtils {

    public static EntityDiff diff(final Object obj, final Object snapshot, Class<?> tClass) {
        if (snapshot == null) {
            throw new RuntimeException("未找到快照！");
        }
        EntityDiff entityDiff = new EntityDiff();
        diff(entityDiff, obj, snapshot, tClass);
        return entityDiff;
    }

    public static void diff(EntityDiff entityDiff, final Object obj, final Object snapshot, Class<?> tClass) {
        Field[] fields = tClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object aggObject = null;
            Object snapshotObject = null;
            try {
                aggObject = field.get(obj);
                snapshotObject = field.get(snapshot);
            } catch (IllegalAccessException e) {
                log.error("IllegalAccessException" + e.getMessage());
            }
            // 校验值对象
            if (Value.class.isAssignableFrom(field.getType())) {
                Value<?> aggFieldValue = (Value<?>) aggObject;
                Value<?> snapshotFieldValue = (Value<?>) snapshotObject;
                if (ValueUtils.isNull(aggFieldValue) && ValueUtils.isNull(snapshotFieldValue)) {
                    continue;
                }
                if (ValueUtils.isNotNull(aggFieldValue) && ValueUtils.isNotNull(snapshotFieldValue)) {
                    if (IdValue.class.isAssignableFrom(field.getType())) {
                        Diff idDiff = new Diff(field.getName(), DiffTypeEnum.ID, aggFieldValue.getValue(), snapshotFieldValue.getValue());
                        entityDiff.putDiff(field.getName(), idDiff);
                    }
                    if (aggFieldValue.getValue().equals(snapshotFieldValue.getValue())) {
                        continue;
                    }
                }
                if (aggFieldValue == null) {
                    log.error("对比对象的属性值null,属性名称：{}", field.getName());
                }
                Diff newDiff = new Diff(field.getName(), DiffTypeEnum.Modified, snapshotFieldValue != null ? snapshotFieldValue.getValue() : null, aggFieldValue.getValue());
                entityDiff.putDiff(field.getName(), newDiff);
                // 本事也被修改
                entityDiff.setSelfModified(true);
                continue;
            }
            //实体类型
            if (Entity.class.isAssignableFrom(field.getType())) {
                EntityDiff entityDiff1 = new EntityDiff();
                if (snapshotObject == aggObject && snapshotObject == null) {
                    continue;
                }
                if (snapshotObject == null) {
                    entityDiff1.setSelfAdded(true);
                    entityDiff1.setNewValue(aggObject);
                    entityDiff.putDiff(field.getName(), entityDiff1);
                    continue;
                }

                diff(entityDiff1, aggObject, snapshotObject, aggObject.getClass());
                entityDiff.putDiff(field.getName(), entityDiff1);
                continue;
            }
            //集合
            if (Collection.class.isAssignableFrom(field.getType())) {
                Collection<?> aggObjectCollection = aggObject == null ? new ArrayList<>() : (Collection<?>) aggObject;
                Collection<?> snapshotObjectCollection = snapshotObject == null ? new ArrayList<>() : (Collection<?>) snapshotObject;
                int size1 = aggObjectCollection.size();
                int size2 = snapshotObjectCollection.size();
                ListDiff listDiff = new ListDiff();
                if (size1 == size2 && size1 == 0) {
                    continue;
                } else if (size1 > 0) {
                    Object aggObjectItem = aggObjectCollection.iterator().next();
                    Class<?> aggObjectItemClass = aggObjectItem.getClass();
                    if (Entity.class.isAssignableFrom(aggObjectItemClass)) {
                        for (Object entity : aggObjectCollection) {
                            Entity<?> entitySource = (Entity<?>) entity;
                            if (ValueUtils.isNull(entitySource.getId())) {
                                Diff newDiff = new Diff(field.getName(), DiffTypeEnum.Added, entity);
                                listDiff.add(newDiff);
                                continue;
                            }
                            Entity<?> entitySnapshot = null;
                            for (Object snapshotEntity : snapshotObjectCollection) {
                                Entity<?> snapshotObj = (Entity<?>) snapshotEntity;
                                if (entitySource.getId().getValue().equals(snapshotObj.getId().getValue())) {
                                    entitySnapshot = snapshotObj;
                                    snapshotObjectCollection.remove(snapshotObj);
                                    break;
                                }
                            }
                            // 匹配到快照
                            if (entitySnapshot != null) {
//                            Diff newDiff = new Diff(field.getName(), DiffType.Modified, entitySnapshot,entitySource);
//                            listDiff.add(newDiff);
                                EntityDiff newDiff = diff(entitySource, entitySnapshot, aggObjectItemClass);
                                newDiff.setDiffType(DiffTypeEnum.Modified);
                                if (!newDiff.isEmpty()) {
                                    listDiff.add(newDiff);
                                }
                            }
                        }
                    }
                }
                for (Object snapshotEntity : snapshotObjectCollection) {
                    Diff newDiff = new Diff(field.getName(), DiffTypeEnum.Removed, snapshotEntity);
                    listDiff.add(newDiff);
                }
                entityDiff.putDiff(field.getName(), listDiff);
            }
        }
    }
}
